////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2010-2011 Kentaro Sekimoto  All rights reserved.
////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////
// LCD Driver for WX-MP3PLCD-001 board (http://www.ejigu.com/ca28/53/p-r-s/)
//      DST1007PH-24 (http://www.aitendo.com/product/3138)
//      LCD controler: ILI9325
//      ILI9325.c (http://www.will-elec.jp/mp3p_lcd_rx62n_kpit20120402.zip)
////////////////////////////////////////////////////////////////////////////

#include <tinyhal.h>
#include "AUTOLCD.h"

void P16_Initialize(void);
void LCD_nRESET(int n);
void P16_LCD_cmd(volatile unsigned short index, volatile unsigned short data);
void P16_LCD_index(volatile unsigned short index);
void P16_LCD_dat(volatile unsigned short data);
unsigned short P16_LCD_read_dat(volatile unsigned short index);

AUTOLCD_Driver g_AUTOLCD_Driver;

static void delayms(volatile UINT32 n)
{
    HAL_Time_Sleep_MicroSeconds_InterruptEnabled(n * 1000);
}

BOOL AUTOLCD_Driver::Initialize()
{
    unsigned short id;
    P16_Initialize();

    LCD_nRESET(1);
    delayms(10);        // Delay 1ms
    LCD_nRESET(0);
    delayms(100);       // Delay 10ms
    LCD_nRESET(1);
    delayms(500);       // Delay 50ms

    id = P16_LCD_read_dat(0x0000);
    if (id == 0x6813) {
        //P16_LCD_cmd(0x00e7, 0x0010);
        //P16_LCD_cmd(0x0000, 0x0001);
        P16_LCD_cmd(0x00e3, 0x3008);
        P16_LCD_cmd(0x00e7, 0x0012);
        P16_LCD_cmd(0x00ef, 0x1231);
        P16_LCD_cmd(0x0001, 0x0100);    // set SS and SM bit
        P16_LCD_cmd(0x0002, 0x0700);    // set 1 line inversion
        P16_LCD_cmd(0x0003, 0x1030);    // set GRAM write direction and BGR=1.
        P16_LCD_cmd(0x0004, 0x0000);    // Resize register
        P16_LCD_cmd(0x0008, 0x0207);    // set the back porch and front porch
        P16_LCD_cmd(0x0009, 0x0000);    // set non-display area refresh g_AUTOLCD_Driver.m_cycle ISC[3:0]
        P16_LCD_cmd(0x000A, 0x0000);    // FMARK function
        P16_LCD_cmd(0x000C, 0x0000);    // RGB interface setting
        P16_LCD_cmd(0x000D, 0x0000);    // Frame marker Position
        P16_LCD_cmd(0x000F, 0x0000);    // RGB interface polarity
        P16_LCD_cmd(0x0010, 0x0000);    // SAP, BT[3:0], AP, DSTB, SLP, STB
        P16_LCD_cmd(0x0011, 0x0007);    // DC1[2:0], DC0[2:0], VC[2:0]
        P16_LCD_cmd(0x0012, 0x0000);    // VREG1OUT voltage
        P16_LCD_cmd(0x0013, 0x0000);    // VDV[4:0] for VCOM amplitude
        delayms(200);                   // Dis-charge capacitor power voltage
        P16_LCD_cmd(0x0010, 0x1490);    // SAP, BT[3:0], AP, DSTB, SLP, STB
        P16_LCD_cmd(0x0011, 0x0221);    // Set DC1[2:0], DC0[2:0], VC[2:0]
        delayms(50);                    // Delay 50ms
        P16_LCD_cmd(0x0012, 0x001c);    // External reference voltage= Vci;
        delayms(50);                    // Delay 50ms
        P16_LCD_cmd(0x0013, 0x0a00);    // VDV[4:0] for VCOM amplitude
        P16_LCD_cmd(0x0029, 0x000f);    // VCM[5:0] for VCOMH
        P16_LCD_cmd(0x002B, 0x000d);    // Set Frame Rate
        delayms(50);                    // Delay 50ms
        P16_LCD_cmd(0x0020, 0x0000);    // GRAM horizontal Address
        P16_LCD_cmd(0x0021, 0x0000);    // GRAM Vertical Address
        delayms(50);                    // Delay 50ms
        P16_LCD_cmd(0x0030, 0x0000);
        P16_LCD_cmd(0x0031, 0x0607);
        P16_LCD_cmd(0x0032, 0x0305);
        P16_LCD_cmd(0x0035, 0x0000);
        P16_LCD_cmd(0x0036, 0x1604);
        P16_LCD_cmd(0x0037, 0x0204);
        P16_LCD_cmd(0x0038, 0x0001);
        P16_LCD_cmd(0x0039, 0x0707);
        P16_LCD_cmd(0x003C, 0x0000);
        P16_LCD_cmd(0x003D, 0x000F);
        delayms(50);                    // Delay 50ms
        P16_LCD_cmd(0x0050, 0x0000);    // Horizontal GRAM Start Address
        P16_LCD_cmd(0x0051, g_AUTOLCD_Config.Width - 1);
        P16_LCD_cmd(0x0052, 0x0000);    // Vertical GRAM Start Address
        P16_LCD_cmd(0x0053, g_AUTOLCD_Config.Height - 1);
        P16_LCD_cmd(0x0060, 0xA700);    // Gate Scan Line
        P16_LCD_cmd(0x0061, 0x0001);    // NDL,VLE, REV
        P16_LCD_cmd(0x006A, 0x0000);    // set scrolling line
        P16_LCD_cmd(0x0080, 0x0000);
        P16_LCD_cmd(0x0081, 0x0000);
        P16_LCD_cmd(0x0082, 0x0000);
        P16_LCD_cmd(0x0083, 0x0000);
        P16_LCD_cmd(0x0084, 0x0000);
        P16_LCD_cmd(0x0085, 0x0000);
        P16_LCD_cmd(0x0090, 0x0010);
        P16_LCD_cmd(0x0092, 0x0600);
        P16_LCD_cmd(0x0093, 0x0003);
        P16_LCD_cmd(0x0095, 0x0110);
        P16_LCD_cmd(0x0097, 0x0000);
        P16_LCD_cmd(0x0098, 0x0000);
        P16_LCD_cmd(0x0007, 0x0133);    // 262K color and display ON
    } else if (id == 0x1580) {
        P16_LCD_cmd(0x0000, 0x0000);
        P16_LCD_cmd(0x0000, 0x0000);
        delayms(100);                   // Delay 100ms
        P16_LCD_cmd(0x0000, 0x0000);
        P16_LCD_cmd(0x0000, 0x0000);
        P16_LCD_cmd(0x0000, 0x0000);
        P16_LCD_cmd(0x0000, 0x0000);
        P16_LCD_cmd(0x00A4, 0x0001);    // CALB=1
        delayms(100);                   // Delay 100ms
        id = P16_LCD_read_dat(0x0000);
        P16_LCD_cmd(0x0060, 0xA700);    // Driver Output Control
        P16_LCD_cmd(0x0008, 0x0808);    // Display Control BP=8, FP=8
        // Gamma Setting
        P16_LCD_cmd(0x0030, 0x0203);    // y control
        P16_LCD_cmd(0x0031, 0x080f);    // y control
        P16_LCD_cmd(0x0032, 0x0401);    // y control
        P16_LCD_cmd(0x0033, 0x050b);    // y control
        P16_LCD_cmd(0x0034, 0x3330);    // y control
        P16_LCD_cmd(0x0035, 0x0b05);    // y control
        P16_LCD_cmd(0x0036, 0x0005);    // y control
        P16_LCD_cmd(0x0037, 0x0f08);    // y control
        P16_LCD_cmd(0x0038, 0x0302);    // y control
        P16_LCD_cmd(0x0039, 0x3033);    // y control
        // Power Setting
        P16_LCD_cmd(0x0090, 0x0018);    // 80Hz
        P16_LCD_cmd(0x0010, 0x0530);    // Power Control
        P16_LCD_cmd(0x0011, 0x0237);
        P16_LCD_cmd(0x0012, 0x01BF);
        P16_LCD_cmd(0x0013, 0x1000);
        delayms(200);                   // Delay 200ms
        P16_LCD_cmd(0x0001, 0x0100);
        P16_LCD_cmd(0x0002, 0x0200);
        P16_LCD_cmd(0x0003, 0x1030);
        P16_LCD_cmd(0x0009, 0x0001);
        P16_LCD_cmd(0x000A, 0x0008);
        P16_LCD_cmd(0x000C, 0x0000);
        P16_LCD_cmd(0x000D, 0xD000);
        P16_LCD_cmd(0x000E, 0x0030);
        P16_LCD_cmd(0x000F, 0x0000);
        P16_LCD_cmd(0x0020, 0x0000);
        P16_LCD_cmd(0x0021, 0x0000);
        P16_LCD_cmd(0x0029, 0x002e);
        P16_LCD_cmd(0x0050, 0x0000);
        P16_LCD_cmd(0x0051, 0x00EF);
        P16_LCD_cmd(0x0052, 0x0000);
        P16_LCD_cmd(0x0053, 0x013F);
        P16_LCD_cmd(0x0061, 0x0001);
        P16_LCD_cmd(0x006A, 0x0000);
        P16_LCD_cmd(0x0080, 0x0000);
        P16_LCD_cmd(0x0081, 0x0000);
        P16_LCD_cmd(0x0082, 0x005F);
        P16_LCD_cmd(0x0093, 0x0701);
        //P16_LCD_cmd(0x0007, 0x0112);
        P16_LCD_cmd(0x0007, 0x0173);
        delayms(400);                   // Delay 1ms
    }
#if 1
    P16_LCD_index(0x0022);
    unsigned int i,n,m;
    unsigned short color[8]={0xf800,0x07e0,0x001f,0x07ff,0xf81f,0xffe0,0x0000,0xffff};
    for(n=0;n<8;n++)
        for(i=0;i<40;i++)
            for(m=0;m<240;m++)
                P16_LCD_dat(0xffff);

    for(n=0;n<8;n++)
        for(i=0;i<40;i++)
            for(m=0;m<240;m++)
                P16_LCD_dat(color[n]);
#endif
}

BOOL AUTOLCD_Driver::Uninitialize()
{
}

void AUTOLCD_Driver::PowerSave(BOOL On)
{
}

void AUTOLCD_Driver::Clear()
{
    UINT32 x;
    g_AUTOLCD_Driver.m_cx = 0;
    g_AUTOLCD_Driver.m_cy = 0;
    P16_LCD_cmd(0x0020, 0);
    P16_LCD_cmd(0x0021, 0);
    P16_LCD_index(0x0022);
    for (x = 0; x < (g_AUTOLCD_Config.Width * g_AUTOLCD_Config.Height); x++){
        P16_LCD_dat(0x0000);
    }
}

void AUTOLCD_Driver::BitBltEx565(int x, int y, int width, int height, UINT32 data[])
{
    int i, j;
    UINT16 v1, v2;
    UINT16 *pdata = (UINT16 *)data;
    for (j = 0; j < height; j ++) {
        P16_LCD_cmd(0x0020, x);
        P16_LCD_cmd(0x0021, y + j);
        P16_LCD_index(0x0022);
        for (i = 0; i < width; i ++) {
            P16_LCD_dat(*(pdata + i));
        }
        pdata += g_AUTOLCD_Config.Width;
    }
}

void AUTOLCD_Driver::BitBltEx( int x, int y, int width, int height, UINT32 data[] )
{
    int i, j;
    UINT16 v1, v2;
    UINT16 *pdata = (UINT16 *)data;
    pdata += (y * g_AUTOLCD_Config.Width + x);
    AUTOLCD_Driver::BitBltEx565(x, y, width, height, (UINT32 *)pdata);
}

void AUTOLCD_Driver::WriteChar_Color(unsigned char c, UINT8 cx, UINT8 cy, UINT16 fgcol, UINT16 bgcol)
{
    UINT8 x, y;
    UINT16 col;
    UINT16 sx = cx * Font_Width() + g_AUTOLCD_Config.TextSx;
    UINT16 sy = cy * Font_Height() + g_AUTOLCD_Config.TextSy;

    unsigned char *font = (unsigned char *)Font_GetGlyph(c);
    for (y = 0; y < Font_Height(); y++) {
        P16_LCD_cmd(0x0020, sx);
        P16_LCD_cmd(0x0021, sy + y);
        P16_LCD_index(0x0022);
        for (x = 0; x < Font_Width(); x++) {
            if (font[y] & (0x80 >> x)) {
                col = fgcol;
            } else {
                col = bgcol;
            }
            P16_LCD_dat(col);
        }
    }
}

void AUTOLCD_Driver::WriteChar(unsigned char c, int cx, int cy)
{
    WriteChar_Color(c, (UINT8)cx, (UINT8)cy, g_AUTOLCD_Config.fcol, g_AUTOLCD_Config.bcol);
}

#define TAB_WIDTH   4
#define TAB_MASK    0xFFFFFFFC

void AUTOLCD_Driver::WriteFormattedChar(unsigned char ch)
{
    UINT16 colums = g_AUTOLCD_Config.Width/Font_Width();
    UINT16 rows = g_AUTOLCD_Config.Height/Font_Height();
    if (ch == 0xc) {
        Clear();
        g_AUTOLCD_Driver.m_cx = 0;
        g_AUTOLCD_Driver.m_cy = 0;
    } else if (ch == '\t') {
        g_AUTOLCD_Driver.m_cx += TAB_WIDTH;
        g_AUTOLCD_Driver.m_cx &= TAB_MASK;
        if (g_AUTOLCD_Driver.m_cx >= colums) {
            g_AUTOLCD_Driver.m_cx = 0;
            g_AUTOLCD_Driver.m_cy ++;
            if (g_AUTOLCD_Driver.m_cy == rows) {
                g_AUTOLCD_Driver.m_cy = 0;
            }
        }
    } else if (ch == '\n') {
        g_AUTOLCD_Driver.m_cy++;
        if (g_AUTOLCD_Driver.m_cy == rows) {
            g_AUTOLCD_Driver.m_cy = 0;
        }
    } else if (ch == '\r') {
        g_AUTOLCD_Driver.m_cx = 0;
    } else {
        WriteChar(ch, g_AUTOLCD_Driver.m_cx, g_AUTOLCD_Driver.m_cy);
        g_AUTOLCD_Driver.m_cx ++;
        if (g_AUTOLCD_Driver.m_cx == colums) {
            g_AUTOLCD_Driver.m_cx = 0;
            g_AUTOLCD_Driver.m_cy ++;
            if (g_AUTOLCD_Driver.m_cy == rows) {
                g_AUTOLCD_Driver.m_cy = 0;
            }
        }
    }
}
